﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GraphViewer.Entity;
using GraphViewer.View;
using System.IO;
using System.Diagnostics;
using System.Configuration;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Xml;
using GraphViewer.Data;
using System.Threading.Tasks;
using System.Threading;

namespace GraphViewer
{
    public class ExecutionGroup
    {
        private GroupEntity _group;
        private DataLoader _dataLoader;
        private string _mtsServerName;
        private string _dataBase;
        private CancellationTokenSource _tokenSource;

        public ExecutionGroup(DataLoader dataLoader, GroupEntity group, string mtsServerName, string dataBase, CancellationTokenSource tokenSource)
        {
            _dataLoader = dataLoader;
            _group = group;
            _mtsServerName = mtsServerName;
            _dataBase = dataBase;
            _tokenSource = tokenSource;
        }

        public void Execute(Task[] tasks)
        {
            Execute();
        }

        public void Execute2()
        {
            string groupFound = ExecutionControl.Instance.Groups.Find(g => g == _group.GroupId);

            if (groupFound != null)
            {
                Console.WriteLine("Grupo " + _group.GroupId);
            }
        }

        public void Execute()
        {
            string groupFound = ExecutionControl.Instance.Groups.Find(g => g == _group.GroupId);

            if (groupFound != null)
            {
                ExecutionControl.Instance.Open(_group.GroupId);
                List<Task> tasks;

                foreach (var process in _dataLoader.Processes.Where(p => p.GroupId == _group.GroupId))
                {
                    if (process.Predecessors.All(s => _dataLoader.Processes.Find(p => p.ProcessId == s).TaskType == "Dummy") && process.TaskType == "Job")
                    {
                        process.TokenSource = new CancellationTokenSource();
                        process.ExecutionProcess = new ExecutionProcess(process, _mtsServerName, _dataBase, false, process.TokenSource);
                        process.Task = new Task(process.ExecutionProcess.Execute, process.TokenSource.Token);
                    }
                }

                foreach (var process in _dataLoader.Processes.Where(p => p.GroupId == _group.GroupId && p.TaskType == "Job" && p.Task == null))
                {
                    tasks = new List<Task>();

                    foreach (string predecessor in process.Predecessors)
                    {
                        ProcessEntity processDependency = _dataLoader.Processes.Find(p => p.ProcessId == predecessor);
                        if (processDependency != null && process.Predecessors.Count > 0 && processDependency.Task != null)
                            tasks.Add(processDependency.Task);
                    }

                    CancellationTokenSource tokenSource = Find(process).TokenSource;
                    process.ExecutionProcess = new ExecutionProcess(process, _mtsServerName, _dataBase, false, tokenSource);
                    process.Task = Task.Factory.ContinueWhenAll(tasks.ToArray(), process.ExecutionProcess.Execute, tokenSource.Token);
                }

                foreach (var process in _dataLoader.Processes.Where(p => p.GroupId == _group.GroupId))
                {
                    if (process.Predecessors.All(s => _dataLoader.Processes.Find(p => p.ProcessId == s).TaskType == "Dummy") && process.TaskType == "Job")
                    {
                        if (_tokenSource.IsCancellationRequested)
                            _tokenSource.Token.ThrowIfCancellationRequested();

                        process.Task.Start();
                    }
                }

                tasks = new List<Task>();

                foreach (var process in _dataLoader.Processes.Where(p => p.GroupId == _group.GroupId))
                {
                    if (All(process.Successors, p => p.TaskType == "Dummy") && process.TaskType == "Job")
                    {
                        tasks.Add(process.Task);
                    }
                }

                try
                {
                    if (_tokenSource.IsCancellationRequested)
                        _tokenSource.Token.ThrowIfCancellationRequested();

                    Task.WaitAll(tasks.ToArray());
                }
                catch
                {
                    _tokenSource.Cancel();
                }
            }
        }

        private bool All(List<string> processes, Func<ProcessEntity, bool> verify)
        {
            bool all = false;
            ProcessEntity found = null;

            foreach (var process in processes)
            {
                found = _dataLoader.Processes.Find(p => p.ProcessId == process);

                if (found != null)
                    all = verify(found);
            }

            return all;
        }

        private ProcessEntity Find(ProcessEntity entity)
        {
            ProcessEntity found = null;

            if (entity.TokenSource != null)
            {
                found = entity;
            }
            else
            {
                foreach (string predecessor in entity.Predecessors)
                {
                    ProcessEntity processDependency = _dataLoader.Processes.Find(p => p.ProcessId == predecessor);

                    if (processDependency != null && processDependency.TokenSource != null)
                    {
                        found = processDependency;
                        break;
                    }
                    else
                    {
                        found = Find(processDependency);
                    }
                }
            }

            return found;
        }
    }
}
